<?php

namespace App\Service\Rbac;

use App\Constants\ErrorCode;
use App\Constants\Rbac\AdminCode;
use App\Constants\Rbac\RouteCode;
use App\Constants\Redis\TempRedisCode;
use App\Model\Rbac\RoleModel;
use App\Model\Rbac\RoleRouteModel;
use App\Model\Rbac\RouteModel;
use App\Service\BaseService;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;

class RbacService extends BaseService
{


    #[Inject]
    protected RouteModel $routeModel;

    #[Inject]
    protected RoleModel $roleModel;

    #[Inject]
    protected RoleRouteModel $roleRouteModel;

    /**
     * @param string $path
     * @param null|int $type
     * @return array|null
     */
    public function getPathInfo($path, $type = null)
    {
        return $this->routeModel->findByPath($path, $type)?->toArray();
    }

    public function allRole()
    {
        return $this->roleModel->getAll()->toArray();
    }

    public function addRole($data)
    {
        $nameEx = $this->roleModel->findByName($data['name']);
        if ($nameEx) {
            return [false, '名称已存在'];
        }
        $r = $this->roleModel->addOne($data);
        if ($r) {
            return [true, '添加成功'];
        }
        return [false, '添加失败'];
    }

    public function updateRole($id, $data)
    {
        $role = $this->roleModel->findById($id,['id','name']);
        if (!$role){
            return [false,'角色不存在'];
        }
        if ($role->name == $data['name']){
            return [false,'没有改变'];
        }
        $r = $this->roleModel->updateName($id, $data['name']);
        if ($r){
            return [true,'更新成功'];
        }
        return [false,'没有改动'];
    }

    public function delRole(array $roleIds)
    {
        $roles = $this->roleModel->getSomeByIds($roleIds, ['id']);
        if ($roles->isEmpty()) {
            return [false, '数据不存在或者已被删除'];
        }
        $r = $this->roleModel->delByIds($roleIds);
        if ($r) {
            $this->roleRouteModel->delRoleAllRoute($roleIds);
            return [true, '删除成功'];
        }
        return [false, '删除失败'];
    }

    public function addRoute(array $data, $adminId = 0)
    {
        // 菜单可以重复path，因为有节点，path为空
        $pathEx = $this->routeModel->findByPath($data['path'], $data['type'], ['id']);
        if ($pathEx){
            return [false, 'path已存在'];
        }
        $lock = TempRedisCode::addRouteLock($adminId);
        $this->lockError($lock);
        $r = $this->routeModel->addOne($data);
        $this->freeLock($lock);
        if ($r) {
            return [true, '添加成功'];
        }
        return [false, '添加失败'];
    }

    public function updateRoute($routeId, array $data)
    {
        $route = $this->routeModel->findById($routeId, ['id']);
        if (!$route) {
            return [false, '数据不存在'];
        }
        $r = $this->routeModel->updateById($routeId, $data);
        if ($r) {
            return [true, '修改成功'];
        }
        return [false, '修改失败'];
    }

    public function delRoutes(array $routeIds)
    {
        $routes = $this->routeModel->getSomeByIds($routeIds, ['id']);
        if ($routes->isEmpty()) {
            return [false, '数据不存在'];
        }
        $r = $this->routeModel->delByIds($routeIds);
        if ($r) {
            return [true, '删除成功'];
        }
        return [false, '删除失败'];
    }

    /**
     * @param array $params
     * @return array[]
     */
    public function getAllRoutes($params = [], $field=['*'])
    {
        return $this->routeModel->getAll($params, $field)->toArray();
    }

    // 角色保存路由 role <=> route
    public function roleSaveRoute($roleId, $routeIds = [])
    {
        $lockKey = TempRedisCode::saveRoleRoutes($roleId);
        $this->lockError($lockKey);
        // 过滤不存在的数据
        $routeIds = $this->routeModel->getSomeByIds($routeIds, ['id'])->pluck('id');
        // 角色已经有的路由
        $exRouteIds = $this->roleRouteModel->getAllRoutesOfRole($roleId)->pluck('route_id');
        // 需要删除的
        $needDelRoute = $exRouteIds->diff($routeIds);
        // 需要新增的
        $needAdd = $routeIds->diff($exRouteIds);
        if ($needDelRoute->isEmpty() && $needAdd->isEmpty()) {
            $this->freeLock($lockKey);
            return [false, '没有改动'];
        }
        Db::beginTransaction();
        try {
            if ($needDelRoute->isNotEmpty()) {
                $r = $this->roleRouteModel->delSomeRouteOfRole($roleId, $needDelRoute);
                if (!$r) {
                    throw new \Exception('移除数据失败', ErrorCode::SERVICE_HAND_FAILED);
                }
            }
            if ($needAdd->isNotEmpty()) {
                $r = $this->roleRouteModel->addMany($roleId, $needAdd->toArray());
                if (!$r) {
                    throw new \Exception('添加数据失败', ErrorCode::SERVICE_HAND_FAILED);
                }
            }
            Db::commit();
            $this->freeLock($lockKey);
            return [true, '操作成功'];
        } catch (\Throwable $e) {
            Db::rollBack();
            $this->freeLock($lockKey);
            return $this->errorMsg($e);
        }
    }

    /**
     * @param int $roleId
     * @param string $ret 01: route_id 只返回路由id | 10: route route详细信息
     * @return array
     */
    public function getAllRoutesOfRole($roleId, string $ret = 'route_id')
    {
        $routeIds = $this->roleRouteModel->getAllRoutesOfRole($roleId);
        if ($routeIds->isEmpty()) {
            return [];
        }
        if ($ret == 'route') {
            return $this->routeModel->getSomeByIds($routeIds, [
                'id',
                'pid',
                'type',
                'name',
                'path',
                'path',
            ])->toArray();
        } else {
            return $routeIds->pluck('route_id')->toArray();
        }
    }

//    public function roleHasAccess($roleId, $routeId)
//    {
//        return $this->roleRouteModel->roleHasRoute($roleId, $routeId);
//    }


    public function anyRoleHasRoute(array $roleIds, $routeId)
    {
        if (empty($roleIds)){
            return false;
        }
        return $this->roleRouteModel->anyRoleHasRoute($roleIds, $routeId);
    }

    /**
     * @param array $roleIds
     * @param string $ret 01: route_id 只返回路由id | 10: route route详细信息
     * @return
     */
    public function getRouteOfSomeRole(array $roleIds,  string $ret = 'route_id')
    {
        $routeIds = $this->roleRouteModel->getRouteIdsOfSomeRole($roleIds);
        if ($ret == 'route') {
            return $this->routeModel->getSomeByIds($routeIds->toArray(), [
                'id',
                'pid',
                'type',
                'name',
                'path',
                'path',
            ])->toArray();
        } else {
            return $routeIds->pluck('route_id')->toArray();
        }
    }

}